package cc.verywell.pureblock.log;
/**
* 
*
* Copyright 2019 TanYaqiu
* @author TanYaqiu 
* @address flat 601
* @date 2019年2月23日 下午7:20:07
*/

import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Pblog.ps = 修改为您的PrintStream;
 */
public class Pblog {

	public static PrintStream ps = System.out;
	
	public static final Boolean SECURE = true; 
	public static final void SECURE(Object... str) {
		print("SECURE", str);
	}
	
	public static final Boolean INIT = true; 
	public static final void INIT(Object... str) {
		print("INIT", str);
	}

	public static final Boolean IO = true; 
	public static final void IO(Object... str) {
		print("IO", str);
	}

	public static final Boolean FS = true; 
	public static final void FS(Object... str) {
		print("FS", str);
	}
	
	public static final Boolean DEBUG = true; 
	public static final void DEBUG(Object... str) {
		print("DEBUG", str);
	}

	private static ReentrantLock lock = new ReentrantLock(true);
	public static void print(final String type,Throwable exp) {
		lock.lock();try {
		printTime();
		ps.print("[");
		ps.print(type);
		ps.print("] ");
		ps.print(exp);
		StackTraceElement ste = getSTE(exp.getStackTrace());
		if(ste!=null) {
			ps.print(" at ");
			ps.print(ste);
		}
		{
			StackTraceElement stethrow = getSTEthrow(exp.getStackTrace()); 
			if(stethrow!=null && stethrow!=ste) {
				ps.print(" throwAt ");
				print(ste,stethrow);
				ste = stethrow;
			}
		}
		StackTraceElement thisste = getSTE(Thread.currentThread().getStackTrace());
		if(thisste!=null && !thisste.getClass().equals(ste.getClass()) || thisste.getLineNumber()!=ste.getLineNumber()) {
			ps.print(" logAt ");
			print(thisste,thisste);
		}
		ps.println();
		ps.flush();
		}finally {
			lock.unlock();
		}
	}
//	public static void print(final String type,String str) {
//		printTime();
//		ps.print("[");
//		ps.print(type);
//		ps.print("] ");
//		ps.print(str);
//		ps.print("\tat ");
//		StackTraceElement ste = getSTE(Thread.currentThread().getStackTrace());
//		ps.print(ste);
//		ps.println();
//	}
	public static void print(final String type,Object... obj) {
		lock.lock();try {
		printTime();
		ps.print("[");
		ps.print(type);
		ps.print("] ");
		StackTraceElement ste=null,newSte;
		for (Object o : obj) {
			if(o instanceof Throwable) {
				Throwable exp = (Throwable)o;
				ps.print(exp);
				newSte = getSTE(exp.getStackTrace());
				if(newSte!=null) {
					ps.print(" at ");
					ste = print(ste,newSte);
				}
				newSte = getSTEthrow(exp.getStackTrace());
				if(newSte!=null && newSte!=ste) {
					ps.print(" throwAt ");
					ste = print(ste,newSte);
				}
			}else if(o instanceof ByteBuffer) {
				printHex((ByteBuffer)o);
			}else if(o instanceof byte[]) {
				printHex((byte[])o);
			}else {
				ps.print(o);
			}
		}
		
		ps.print(" logAt ");
		newSte = getSTE(Thread.currentThread().getStackTrace());
		print(newSte,newSte);
		ps.println();
//		for (StackTraceElement t : Thread.currentThread().getStackTrace()) {
//			ps.print("\t");
//			ps.println(t);
//		}
		ps.flush();
		}finally {
			lock.unlock();
		}
	}
	
	private static StackTraceElement getSTE(StackTraceElement[] stes) {
		for (StackTraceElement ste : stes) {
			if(!Thread.class.getName().equals(ste.getClassName()) && !Pblog.class.getName().equals(ste.getClassName()) && ste.getFileName()!=null && ste.getLineNumber()>=0) {
				return ste;
			}
		}
		return null;
	}
	private static final String CLASS_START;
	//private static final String PROJECT_START;
	static {
		String s = Pblog.class.getName();
		CLASS_START = s.substring(0, s.indexOf('.', s.indexOf('.')+1)+1);
		//PROJECT_START = s.substring(0, s.indexOf('.', CLASS_START.length())+1);
	}
	private static StackTraceElement getSTEthrow(StackTraceElement[] stes) {
		for (StackTraceElement ste : stes) {
			if(ste.getClassName()!=null && ste.getClassName().startsWith(CLASS_START)) {
				return ste;
			}
		}
		return null;
	}
	public static StackTraceElement getSTEwithoutClass(StackTraceElement[] stes,Class<?> withoutClass) {
		for (StackTraceElement ste : stes) {
			if(!Thread.class.getName().equals(ste.getClassName()) && !withoutClass.getName().equals(ste.getClassName()) && ste.getFileName()!=null && ste.getLineNumber()>=0) {
				return ste;
			}
		}
		return null;
	}
	private static StackTraceElement print(StackTraceElement before, StackTraceElement current) {
		if(before!=null) {
			String fileName = current.getFileName();
			if(fileName!=null && fileName.equals(before.getFileName())) {
				int lineNumber = current.getLineNumber();
				ps.print("(");
				ps.print(fileName);
				ps.print(":");
				ps.print(lineNumber);
				ps.print(")");
				return current;
			}
		}
		/*
		 * 如果这里简化了className，Eclipse的导航会识别不出来
		String s = String.valueOf(current);
		if(s.startsWith(PROJECT_START)) {
			s = s.substring(PROJECT_START.length());
			ps.print('.');
		}
		ps.print(s);
		 */
		ps.print(current);
		return current;
	}
	
	private static final SimpleDateFormat tformat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss.SSS"); 
	private static void printTime() {
		ps.print(tformat.format(new Date()));
	}
	
	private static final char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
	public static String byteArray2Hex(byte[] bytes) {
	    StringBuilder sb = new StringBuilder(bytes.length * 2);
	    for (final byte b : bytes) {
	        sb.append(hex[(b & 0xF0) >> 4]);
	        sb.append(hex[b & 0x0F]);
	    }
	    return sb.toString();
	}
	private static void printHex(byte[] bytes) {
	    for (final byte b : bytes) {
	    	ps.print(hex[(b & 0xF0) >> 4]);
	    	ps.print(hex[b & 0x0F]);
	    }
	}
	private static void printHex(ByteBuffer bb) {
		int aof = bb.arrayOffset();
		ps.print('p');
		ps.print(bb.position());
		if(aof!=0) {
			if(aof>0)ps.print('+');
			ps.print(aof);
		}
		ps.print('e');
		ps.print(bb.limit());
		if(aof!=0) {
			if(aof>0)ps.print('+');
			ps.print(aof);
		}
		ps.print('c');
		ps.print(bb.capacity());
		byte[] bytes = bb.array();
		if(bytes.length!=bb.capacity()) {
			if(bytes.length>bb.capacity())ps.print('+');
			ps.print(bytes.length-bb.capacity());
		}
		ps.print('[');
		int end = aof+bb.limit();
		for (int i = aof+bb.position(); i < end; i++) {
			byte b = bytes[i];
			ps.print(hex[(b & 0xF0) >> 4]);
	    	ps.print(hex[b & 0x0F]);
		}
		ps.print(']');
	}
	
	public Pblog() {
		// TODO Auto-generated constructor stub
	}
	
	


}
